package topopt;

import fem2.AbstractStructuralStaticDemo;
import fem2.Debugger;
import fem2.Element;
import fem2.MaterialModel;
import fem2.MeshPart;
import fem2.Model;
import fem2.Observer;
import fem2.ParallelAssembler;
import fem2.analysis.Analysis;
import fem2.analysis.LinearStaticAnalysis;
import fem2.element.StructuralElement;
import fem2.enu.EchoLevelType;
import fem2.enu.State;
import fem2.material.ElasticMaterialModel;
import fem2.material.StVenantKirchhoffMM;
import fem2.material.TopOptFilter;
import fem2.material.TopOptMaterialModel;
import fem2.observer.GidStressObserver;
import fem2.observer.GidTopOptDensityObserver;
import fem2.observer.StrainEnergyObserver;
import fem2.observer.TimeStepObserver;
import fem2.observer.TimingObserver;
import fem2.pre_and_post.GidPost;
import fem2.strategies.Strategy;
import fem2.strategies.TopOptStrategy;

public abstract class AbstractTopOptDemo extends AbstractStructuralStaticDemo {

	public AbstractTopOptDemo() {
		projectDir = "/home/hbui/kratos_janosch";
	}

	public abstract TopOptFilter createFilter(Model m);

	@Override
	public MaterialModel createMaterial(Model m) {
		double E = 1.0;
		double nu = 0.3;
		double t = 1.0;
		ElasticMaterialModel mm = new StVenantKirchhoffMM(E, nu, t, 0.0, State.PLANE_STRESS);
		double Emin = 1e-9;
		double volfrac = 0.5;
		double penal = 3;

		TopOptFilter filter = createFilter(m);

		MaterialModel topmm = new TopOptMaterialModel(mm, E, Emin, penal, volfrac, filter);

		return topmm;
	}

	@Override
	public Element createElement(MeshPart mp, MaterialModel mm) {
		return new StructuralElement(mp, mm);
	}

	@Override
	public void run() {
		Analysis an = createAnalysis();
		an.run();
		postAnalysis();
	}

	@Override
	public void run(int nt) {
		Analysis an = createAnalysis();
		an.getStrategy().setNumberOfTimeStep(nt);
		an.run();
		postAnalysis();
	}

	@Override
	public void run(double[] lambda) {
		throw new Error("this type of analysis is not supported");
	}

	private Analysis createAnalysis() {
		Model m = createModel();
		m.setAssembler(new ParallelAssembler(m, 4));

		TopOptMaterialModel topmm = (TopOptMaterialModel) ((StructuralElement) m.getElement(0))
				.getMaterialModel();

		Analysis an = new LinearStaticAnalysis(m);
		an.setStrategy(new TopOptStrategy(m, 0.01));
		Strategy s = an.getStrategy();
		TimeStepObserver tso = (TimeStepObserver) s.addObserver(new TimeStepObserver());
		s.setEchoLevel(EchoLevelType.OUTPUT);
		s.addObserver(topmm);
		s.addObserver(new TimingObserver());
		s.addObserver(new StrainEnergyObserver(m));

		if (meshFileName != null) {
			if (projectName != null) {
				String meshName = meshFileName.split("\\.")[0];
				GidPost post = new GidPost(projectDir, projectName, meshName);
				post.clearAllPostFiles();
				// post.clearAllPostFiles(meshName);
				Observer so = new GidStressObserver(m, tso, projectDir, projectName, meshName);
				Observer to = new GidTopOptDensityObserver(m, tso, projectDir, projectName,
						meshName);
				s.addObserver(so);
				s.addObserver(to);
			}
		} else {
			if (projectName != null) {
				GidPost post = new GidPost(projectDir, projectName);
				post.clearAllPostFiles();
				Observer so = new GidStressObserver(m, tso, projectDir, projectName);
				Observer to = new GidTopOptDensityObserver(m, tso, projectDir, projectName);
				s.addObserver(so);
				s.addObserver(to);
			}
		}

		return an;
	}

	private void postAnalysis() {
		Debugger.watch("Analysis completed");

		if (meshFileName != null) {
			/*
			 * clean all previous post files
			 */
			// String meshName = meshFileName.split("\\.")[0];
			// GidPost post = new GidPost(projDir, projName, meshName);
			// post.moveAllPostFiles(meshName);
		}
	}
}
